7.4: Services
Contents:
- Introduction
- What is a service?
- Declaring services in the manifest
- Started services
- Bound services
- Service lifecycle
- Foreground services
- Scheduled services
- Learn More
In this chapter you learn about the different types of services, how to use them, and how to manage their lifecycles within your app.
What is a service?
A service is an application component that performs long-running operations, usually in the background. A service doesn't provide a user interface (UI). (An activity, on the other hand, provides a UI.)
A service can be started, bound, or both:
A started service is a service that an application component starts by calling
startService()
.Use started services for tasks that run in the background to perform long-running operations. Also use started services for tasks that perform work for remote processes.
A bound service is a service that an application component binds to itself by calling
bindService()
.Use bound services for tasks that another app component interacts with to perform interprocess communication (IPC). For example, a bound service might handle network transactions, perform file I/O, play music, or interact with a content provider.
If your service is going to do any CPU-intensive work or blocking operations (such as MP3 playback or networking), create a new thread within the service to do that work. By using a separate thread, you reduce the risk of Application Not Responding (ANR) errors, and the application's main thread can remain dedicated to user interaction with your activities.
To implement any kind of service in your app:
- Declare the service in the manifest.
- Create implementation code, as described in Started services and Bound services, below.
- Manage the service lifecycle.
Declaring services in the manifest
As with activities and other components, you must declare all services in your application's manifest file. To declare a service, add a <service>
element as a child of the <application>
element. For example:
<manifest ... >
...
<application ... >
<service android:name="ExampleService"
android:exported="false" />
...
</application>
</manifest>
To block access to a service from other applications, declare the service as private. To do this, set the android:exported
attribute to false
. This stops other apps from starting your service, even when they use an explicit intent.
Started services
How a service starts:
- An application component such as an activity calls
startService()
and passes in anIntent
. TheIntent
specifies the service and includes any data for the service to use. - The system calls the service's
onCreate()
method and any other appropriate callbacks on the main thread. It's up to the service to implement these callbacks with the appropriate behavior, such as creating a secondary thread in which to work. - The system calls the service's
onStartCommand()
method, passing in theIntent
supplied by the client in step 1. (The client in this context is the application component that calls the service.)
Once started, a service can run in the background indefinitely, even if the component that started it is destroyed. Usually, a started service performs a single operation and does not return a result to the caller. For example, it might download or upload a file over the network. When the operation is done, the service should stop itself by calling stopSelf()
, or another component can stop it by calling stopService()
.
For instance, suppose an activity needs to save data to an online database. The activity starts a companion service by passing an Intent
to startService()
. The service receives the intent in onStartCommand()
, connects to the Internet, and performs the database transaction. When the transaction is done, the service uses stopSelf()
to stop itself and is destroyed. (This is an example of a service you want to run in a worker thread instead of the main thread.)
IntentService
Most started services don't need to handle multiple requests simultaneously, and if they did it could be a dangerous multi-threading scenario. For this reason, it's probably best if you implement your service using the IntentService
class.
IntentService
is a useful subclass of Service
:
IntentService
automatically provides a worker thread to handle yourIntent
.IntentService
handles some of the boilerplate code that regular services need (such as starting and stopping the service).IntentService
can create a work queue that passes one intent at a time to youronHandleIntent()
implementation, so you don't have to worry about multi-threading.
To implement IntentService
:
- Provide a small constructor for the service.
- Create an implementation of
onHandleIntent()
to do the work that the client provides.
Here's an example implementation of IntentService
:
public class HelloIntentService extends IntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
@Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
}
}
Bound services
A service is "bound" when an application component binds to it by calling bindService()
. A bound service offers a client-server interface that allows components to interact with the service, send requests, and get results, sometimes using interprocess communication (IPC) to send and receive information across processes. A bound service runs only as long as another application component is bound to it. Multiple components can bind to the service at once, but when all of them unbind, the service is destroyed.
A bound service generally does not allow components to start it by calling startService()
.
Implementing a bound service
To implement a bound service, define the interface that specifies how a client can communicate with the service. This interface, which your service returns from the onBind()
callback method, must be an implementation of IBinder
.
To retrieve the IBinder
interface, a client application component calls bindService()
. Once the client receives the IBinder
, the client interacts with the service through that interface.
There are multiple ways to implement a bound service, and the implementation is more complicated than a started service. For complete details about bound services, see Bound Services.
Binding to a service
To bind to a service that is declared in the manifest and implemented by an app component, use bindService()
with an explicit Intent
.
bindService()
with an implicit Intent
.
Service lifecycle
The lifecycle of a service is simpler than that of an activity. However, it's even more important that you pay close attention to how your service is created and destroyed. Because a service has no UI, services can continue to run in the background with no way for the user to know, even if the user switches to another application. This consumes resources and drains battery.
Like an activity, a service has lifecycle callback methods that you can implement to monitor changes in the service's state and perform work at the appropriate times. The following skeleton service demonstrates each of the lifecycle methods:
public class ExampleService extends Service {
int mStartMode; // indicates how to behave if the service is killed
IBinder mBinder; // interface for clients that bind
boolean mAllowRebind; // indicates whether onRebind should be used
@Override
public void onCreate() {
// The service is being created
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// The service is starting, due to a call to startService()
return mStartMode;
}
@Override
public IBinder onBind(Intent intent) {
// A client is binding to the service with bindService()
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
// All clients have unbound with unbindService()
return mAllowRebind;
}
@Override
public void onRebind(Intent intent) {
// A client is binding to the service with bindService(),
// after onUnbind() has already been called
}
@Override
public void onDestroy() {
// The service is no longer used and is being destroyed
}
}
Lifecycle of started services vs. bound services
A bound service exists only to serve the application component that's bound to it, so when no more components are bound to the service, the system destroys it. Bound services don't need to be explicitly stopped the way started services do (using stopService()
or stopSelf()
).
The diagram below shows a comparison between the started and bound service lifecycles.
Foreground services
While most services run in the background, some run in the foreground. A foreground service is a service that the user is aware of, so it's not a candidate for the system to kill when low on memory.
For example, a music player that plays music from a service should be set to run in the foreground, because the user is aware of its operation. The notification in the status bar might indicate the current song and allow the user to launch an activity to interact with the music player.
To request that a service run in the foreground, call startForeground()
instead of startService()
. This method takes two parameters: an integer that uniquely identifies the notification and the Notification
for the status bar. This notification is ongoing, meaning that it can't be dismissed. It stays in the status bar until the service is stopped or removed from the foreground.
For example:
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Hello World!");
startForeground(ONGOING_NOTIFICATION_ID, mBuilder.build());
startForeground()
must not be 0.
To remove the service from the foreground, call stopForeground()
. This method takes a boolean, indicating whether to remove the status bar notification. This method doesn't stop the service. However, if you stop the service while it's still running in the foreground, then the notification is also removed.
Scheduled services
For API level 21 and higher, you can launch services using the JobScheduler
API. To use JobScheduler
, you need to register jobs and specify their requirements for network and timing. The system schedules jobs for execution at appropriate times.
The JobScheduler
interface provides many methods to define service-execution conditions. For details, see the JobScheduler reference
.